CFLAGS ?= -O2
CXXFLAGS ?= -O2
SHELLCHECK ?= shellcheck
CRONTAB ?= crontab
PCH ?= y

version			:= @version@
schedules		:= @schedules@
schedules_not		:= @schedules_not@
enable_runparts		:= @enable_runparts@
have_onsuccess		:= @have_onsuccess@
use_loglevelmax		:= @use_loglevelmax@
part2timer		:= @part2timer@
crond2timer		:= @crond2timer@
pamname			:= @pamname@

prefix		:= @prefix@
bindir		:= @bindir@
datadir		:= @datadir@
libdir		:= @libdir@
libexecdir	:= @libexecdir@
statedir	:= @statedir@
mandir		:= @mandir@
docdir		:= @docdir@
unitdir		:= @unitdir@
generatordir	:= @generatordir@
sysusersdir	:= @sysusersdir@

srcdir		:= $(CURDIR)/src
outdir		:= $(CURDIR)/out
builddir	:= $(outdir)/build
distname	:= systemd-cron-$(version)
distdir		:= $(outdir)/dist/$(distname)
tarball		:= $(outdir)/dist/$(distname).tar.xz

ifneq ($(enable_runparts),no)
out_services	:= $(foreach schedule,$(schedules),$(builddir)/units/cron-$(schedule).service)
out_timers		:= $(foreach schedule,$(schedules),$(builddir)/units/cron-$(schedule).timer)
out_targets		:= $(foreach schedule,$(schedules),$(builddir)/units/cron-$(schedule).target)
else
out_services	:= $(builddir)/units/systemd-cron-cleaner.service
out_timers	:= $(builddir)/units/systemd-cron-cleaner.timer
out_targets	:=
endif
out_units		:= $(out_services) $(out_timers) $(out_targets) $(builddir)/units/cron.target \
                           $(builddir)/units/cron-update.path $(builddir)/units/cron-update.service \
                           $(builddir)/units/cron-mail@.service
out_manuals		:= $(patsubst $(srcdir)/man/%.in,$(builddir)/man/%,$(wildcard $(srcdir)/man/*))
out_programs_sh		:= $(subst $(srcdir),$(builddir),$(patsubst %.sh,%,$(wildcard $(srcdir)/bin/*.sh)))
out_programs_cpp	:= $(subst $(srcdir),$(builddir),$(patsubst %.cpp,%,$(wildcard $(srcdir)/bin/*.cpp)))
out_programs_c		:= $(subst $(srcdir),$(builddir),$(patsubst %.c,%,$(wildcard $(srcdir)/bin/*.c)))
outputs			:= $(out_units) $(out_manuals) $(out_programs_sh) $(out_programs_cpp) $(out_programs_c)

define \n


endef

null :=
ifneq ($(enable_runparts),no)
requires = $(subst ${null} ${null},\n,$(foreach schedule,$(schedules),Requires=cron-$(schedule).timer))
else
requires = Requires=systemd-cron-cleaner.timer
endif

# $(call in2out,$input,$output,$schedule,$requires)
define in2out

	sed \
		-e "s|\@statedir\@|$(statedir)|g" \
		-e "s|\@libexecdir\@|$(libexecdir)|g" \
		-e "s|\@version\@|$(version)|g" \
		-e "s|\@use_loglevelmax\@|$(use_loglevelmax)|g" \
		-e "s|\@enable_runparts\@|$(enable_runparts)|g" \
		-e "$(if $(filter $(have_onsuccess),yes),s|\@remove_if_no_onsuccess\@||g,/\@remove_if_no_onsuccess\@/d)" \
		-e "s|\@schedule\@|$3|g" \
		-e "s|\@requires\@|$4|g" \
		$1 > $2
endef

all: $(builddir) $(outputs)

clean:
	rm -rf $(outdir)

distprep: $(distdir)
	cp -at $(distdir) configure Makefile.in LICENSE README.md VERSION src test

dist: $(tarball)

test: test-unshare test-nounshare
test-unshare: all
	unshare -rm test/test-generator $(enable_runparts) "$(statedir)" $(have_onsuccess)
	unshare -ru test/test-m_f_j $(version)
	unshare -rm test/test-r_s_s
test-nounshare: all
	$(SHELLCHECK) $(out_programs_sh) configure
	$(foreach manpage,$(out_manuals),\
		man --warnings --encoding=utf8 --local-file $(manpage) 2>&1 > /dev/null${\n})

install: all
	install -m755 -D $(builddir)/bin/crontab $(DESTDIR)$(bindir)/$(CRONTAB)
	install -m755 -D $(builddir)/bin/systemd-crontab-generator $(DESTDIR)$(generatordir)/systemd-crontab-generator
	install -m755 -D $(builddir)/bin/remove_stale_stamps $(DESTDIR)$(libexecdir)/systemd-cron/remove_stale_stamps
	install -m755 -D $(builddir)/bin/mail_for_job $(DESTDIR)$(libexecdir)/systemd-cron/mail_for_job
	install -m755 -D $(builddir)/bin/boot_delay $(DESTDIR)$(libexecdir)/systemd-cron/boot_delay
	install -m644 -D $(srcdir)/lib/sysusers.d/systemd-cron.conf $(DESTDIR)$(sysusersdir)/systemd-cron.conf
	install -m755 -D $(builddir)/bin/crontab_setgid $(DESTDIR)$(libexecdir)/systemd-cron/crontab_setgid

	install -m644 -D $(builddir)/man/systemd.cron.7 $(DESTDIR)$(mandir)/man7/systemd.cron.7
	install -m644 -D $(builddir)/man/crontab.1 $(DESTDIR)$(mandir)/man1/$(CRONTAB).1
	install -m644 -D $(builddir)/man/crontab.5 $(DESTDIR)$(mandir)/man5/$(CRONTAB).5
	install -m644 -D $(builddir)/man/anacrontab.5 $(DESTDIR)$(mandir)/man5/anacrontab.5
	install -m644 -D $(builddir)/man/systemd-crontab-generator.8 $(DESTDIR)$(mandir)/man8/systemd-crontab-generator.8

	install -m644 -D $(builddir)/units/cron.target $(DESTDIR)$(unitdir)/cron.target
	install -m644 $(builddir)/units/cron-update.path $(DESTDIR)$(unitdir)
	install -m644 $(builddir)/units/cron-update.service $(DESTDIR)$(unitdir)
	install -m644 $(builddir)/units/cron-mail@.service $(DESTDIR)$(unitdir)

ifneq ($(enable_runparts),no)
	$(foreach schedule,$(schedules),\
		install -m644 $(builddir)/units/cron-$(schedule).timer $(DESTDIR)$(unitdir)${\n})
	$(foreach schedule,$(schedules),\
		install -m644 $(builddir)/units/cron-$(schedule).target $(DESTDIR)$(unitdir)${\n})
	$(foreach schedule,$(schedules),\
		install -m644 $(builddir)/units/cron-$(schedule).service $(DESTDIR)$(unitdir)${\n})
else
	install -m644 $(builddir)/units/systemd-cron-cleaner.timer $(DESTDIR)$(unitdir)
	install -m644 $(builddir)/units/systemd-cron-cleaner.service $(DESTDIR)$(unitdir)
endif

$(builddir)/units/cron-update.path: $(srcdir)/units/cron-update.path.in
	$(call in2out,$<,$@)
ifeq ($(enable_runparts),no)
	echo 'PathChanged=/etc/cron.hourly' >> $@
	echo 'PathChanged=/etc/cron.daily' >> $@
	echo 'PathChanged=/etc/cron.weekly' >> $@
	echo 'PathChanged=/etc/cron.monthly' >> $@
endif

$(builddir)/units/cron-update.service: $(srcdir)/units/cron-update.service.in
	$(call in2out,$<,$@)

$(builddir)/units/cron-mail@.service: $(srcdir)/units/cron-mail@.service.in
	$(call in2out,$<,$@)

$(builddir)/units/cron-%.service: $(srcdir)/units/cron-schedule.service.in
	$(call in2out,$<,$@,$*)
ifeq ($(use_loglevelmax),no)
	sed -i -e '/^LogLevelMax=/d' $@
endif

$(builddir)/units/cron-boot.service: $(srcdir)/units/cron-boot.service.in
	$(call in2out,$<,$@,boot)

$(builddir)/units/cron-%.timer: $(srcdir)/units/cron-schedule.timer.in
	$(call in2out,$<,$@,$*)

$(builddir)/units/cron-boot.timer: $(srcdir)/units/cron-boot.timer.in
	$(call in2out,$<,$@,boot)

$(builddir)/units/cron-%.target: $(srcdir)/units/cron-schedule.target.in
	$(call in2out,$<,$@,$*)

$(builddir)/units/cron.target: $(srcdir)/units/cron.target.in
	$(call in2out,$<,$@,,$(requires))

$(builddir)/units/systemd-cron-cleaner.service: $(srcdir)/units/systemd-cron-cleaner.service.in
	$(call in2out,$<,$@)

$(builddir)/units/systemd-cron-cleaner.timer: $(srcdir)/units/systemd-cron-cleaner.timer.in
	$(call in2out,$<,$@)

$(builddir)/man/%: $(srcdir)/man/%.in
	$(call in2out,$<,$@)

$(builddir)/man/systemd.cron.7: $(srcdir)/man/systemd.cron.7.in
	$(call in2out,$<,$@)
ifneq ($(enable_runparts),yes)
	sed -i $(foreach sched,$(schedules)    ,-e '1i.nr timer_$(sched) 0') $@
else
	sed -i $(foreach sched,$(schedules)    ,-e '1i.nr timer_$(sched) 1') $@
endif

ifneq ($(schedules_not),)
	sed -i $(foreach sched,$(schedules_not),-e '1i.nr timer_$(sched) 0') $@
endif

	sed -i $(foreach sched,$(schedules)    ,-e '1i.nr etccron_$(sched) 1') $@

ifneq ($(schedules_not),)
	sed -i $(foreach sched,$(schedules_not),-e '1i.nr etccron_$(sched) 0') $@
endif

$(builddir)/include/part2timer.hpp : $(part2timer)
	LC_LANG=C sort $< | awk '/^#/ || /^$$/ {next}  { print "{\"" $$1 "\"sv, \"" $$2 "\"sv}," }' > $@
$(builddir)/include/crond2timer.hpp : $(crond2timer)
	LC_LANG=C sort $< | awk '/^#/ || /^$$/ {next}  { print "{\"" $$1 "\"sv, \"" $$2 "\"sv}," }' > $@

$(builddir)/include/configuration.hpp : $(srcdir)/include/configuration.hpp.in
	mkdir -p $(builddir)/include
	sed -e 's:\@use_loglevelmax\@:$(use_loglevelmax):' \
	    -e 's:\@use_runparts\@:$(if $(filter $(enable_runparts),yes),true,false):' \
	    -e 's:\@version\@:$(version):' \
	    -e 's:\@libexecdir\@:$(libexecdir):' \
	    -e 's:\@statedir\@:$(statedir):' \
	    -e 's:\@pamname\@:$(pamname):g' \
	    -e 's:\@have_onsuccess\@:$(if $(filter $(have_onsuccess),yes),true,false):g' $< > $@

$(builddir)/include/%.hpp: $(srcdir)/include/%.hpp
	cp $< $@

CXXVER := $(shell $(CXX) --version | { read -r l; echo "$$l"; })
ifneq "$(findstring clang,$(CXXVER))" ""
	# clang doesn't use PCHs automatically
	PCH_ARG := $(if $(PCH),-include-pch $(builddir)/include/libvoreutils.hpp.gch) -Wno-gcc-compat
else
	PCH_ARG :=
endif

common_headers := $(builddir)/include/configuration.hpp $(builddir)/include/libvoreutils.hpp$(if $(PCH),.gch) $(builddir)/include/util.hpp
CFLAGS += -Wall -Wextra -fno-exceptions -Wno-psabi
CXXFLAGS += -Wall -Wextra -fno-exceptions -Wno-psabi
$(builddir)/include/libvoreutils.hpp.gch : $(builddir)/include/libvoreutils.hpp
	$(CXX) $(CXXFLAGS) $(CPPFLAGS) -std=c++20 -I $(builddir)/include            $< -o $@

$(builddir)/bin/systemd-crontab-generator: $(srcdir)/bin/systemd-crontab-generator.cpp $(common_headers) $(builddir)/include/part2timer.hpp $(builddir)/include/crond2timer.hpp
	$(CXX) $(CXXFLAGS) $(CPPFLAGS) -std=c++20 -I $(builddir)/include $(PCH_ARG) $< -o $@ $(LDFLAGS) @libmd@

$(builddir)/bin/crontab: $(srcdir)/bin/crontab.cpp $(common_headers)
	$(CXX) $(CXXFLAGS) $(CPPFLAGS) -std=c++20 -I $(builddir)/include $(PCH_ARG) $< -o $@ $(LDFLAGS)

$(builddir)/bin/%: $(srcdir)/bin/%.sh
	$(call in2out,$<,$@)
	chmod +x $@

$(builddir)/bin/crontab_setgid: $(srcdir)/bin/crontab_setgid.c
	$(CC) $(CFLAGS) $(CPPFLAGS) $< -DCRONTAB_DIR='"$(statedir)"' -o $@ $(LDFLAGS)

$(outputs): | $(builddir)

$(outdir):
	mkdir -p $@

$(builddir):
	mkdir -p $@/bin $@/man $@/units $@/include

$(distdir):
	mkdir -p $(distdir)

$(tarball): distprep
	cd $(distdir)/..; tar -cJ --owner=root --group=root --file $(tarball) $(distname)

.PHONY: all clean dist distprep install
